<script setup lang="ts">
import { computed, reactive } from 'vue';
import { loginModuleRecord } from '@/constants/app';
import { useAuthStore } from '@/store/modules/auth';
import { useRouterPush } from '@/hooks/common/router';
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
import { getServiceBaseURL } from '@/utils/service';
import { $t } from '@/locales';

// 将字符串转换为ArrayBuffer
async function strToBuffer(str: string): Promise<ArrayBuffer> {
  const encoder = new TextEncoder();
  return encoder.encode(str).buffer;
}

// 将ArrayBuffer转换为十六进制字符串
function bufferToHex(buffer: ArrayBuffer): string {
  return Array.from(new Uint8Array(buffer))
    .map(b => b.toString(16).padStart(2, '0'))
    .join('');
}

// 密码哈希函数：使用SHA-256哈希
async function hashPassword(password: string): Promise<string> {
  const buffer = await strToBuffer(password);
  const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
  return bufferToHex(hashBuffer);
}

// 对称加密函数：使用AES-GCM加密
async function encryptData(data: string, key: string): Promise<string> {
  try {
    // 生成密钥（将时间戳密钥转换为16字节）
    const keyBuffer = await strToBuffer(key.padStart(16, '0').substring(0, 16));
    const cryptoKey = await crypto.subtle.importKey('raw', keyBuffer, { name: 'AES-GCM' }, false, ['encrypt']);

    // 生成随机初始化向量
    const iv = crypto.getRandomValues(new Uint8Array(12));

    // 加密数据
    const dataBuffer = await strToBuffer(data);
    const encryptedBuffer = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, cryptoKey, dataBuffer);

    // 组合IV和密文并转换为十六进制
    const combined = new Uint8Array(iv.length + encryptedBuffer.byteLength);
    combined.set(iv);
    combined.set(new Uint8Array(encryptedBuffer), iv.length);

    return bufferToHex(combined.buffer);
  } catch {
    // 生产环境中可以使用错误监控系统
    return data;
  }
}

// 密码加密函数：先哈希，再使用时间戳密钥加密
async function encryptPassword(password: string): Promise<string> {
  // 1. 先哈希密码
  const hashedPassword = await hashPassword(password);
  // console.log('hashedPassword:', hashedPassword); // 调试信息已注释

  // 2. 生成时间戳密钥（时间戳整除30000）
  const timestampKey = Math.floor(Date.now() / 30000).toString();

  // 3. 使用时间戳密钥加密哈希后的密码
  const encryptedPassword = await encryptData(hashedPassword, timestampKey);

  return encryptedPassword;
}

defineOptions({
  name: 'PwdLogin'
});

const authStore = useAuthStore();
const { toggleLoginModule } = useRouterPush();
const { formRef, validate } = useNaiveForm();
const isHttpProxy = import.meta.env.DEV && import.meta.env.VITE_HTTP_PROXY === 'Y';
const { baseURL } = getServiceBaseURL(import.meta.env, isHttpProxy);

interface FormModel {
  username: string;
  password: string;
  captcha: string;
}

const model: FormModel = reactive({
  username: '',
  password: '',
  captcha: ''
});

const captchaData = reactive({ src: `${baseURL}/api/getCaptcha` });

const rules = computed<Record<keyof FormModel, App.Global.FormRule[]>>(() => {
  // inside computed to make locale reactive, if not apply i18n, you can define it without computed
  const { formRules, createRequiredRule } = useFormRules();

  return {
    username: formRules.username,
    password: formRules.pwd,
    captcha: [
      createRequiredRule($t('form.code.required')),
      {
        pattern: /^[a-zA-Z0-9]{4,6}$/,
        message: $t('form.code.invalid'),
        trigger: 'change'
      }
    ]
  };
});

async function handleSubmit() {
  await validate();
  // 使用加密函数处理密码
  const encryptedPassword = await encryptPassword(model.password);
  const isSuccess = await authStore.login({
    username: model.username,
    password: encryptedPassword,
    captcha: model.captcha
  });

  // 如果登录失败，则刷新验证码和清空输入框
  if (!isSuccess) {
    getCaptcha();
    model.captcha = '';
    model.password = '';
    model.username = '';
  }
}

async function getCaptcha() {
  const timestamp = new Date().getTime();
  captchaData.src = `${baseURL}/api/getCaptcha?t=${timestamp}`;
}

function handleForgotPassword() {
  toggleLoginModule('reset-pwd');
}
</script>

<template>
  <NForm ref="formRef" :model="model" :rules="rules" size="large" :show-label="false" @keyup.enter="handleSubmit">
    <NFormItem path="userName">
      <NInput v-model:value="model.username" :placeholder="$t('page.login.common.userNamePlaceholder')" />
    </NFormItem>
    <NFormItem path="password">
      <NInput
        v-model:value="model.password"
        type="password"
        show-password-on="click"
        :placeholder="$t('page.login.common.passwordPlaceholder')"
      />
    </NFormItem>
    <NFormItem path="captcha">
      <div class="w-full flex-y-center gap-16px">
        <NInput v-model:value="model.captcha" :placeholder="$t('page.login.common.codePlaceholder')" />
        <NImage :src="captchaData.src" preview-disabled @click="getCaptcha">
          <template #error>
            <div class="captcha-error">⚠️</div>
          </template>
        </NImage>
      </div>
    </NFormItem>

    <NSpace vertical :size="24">
      <div class="flex-y-center justify-between">
        <NCheckbox>{{ $t('page.login.pwdLogin.rememberMe') }}</NCheckbox>
        <a class="cursor-pointer text-primary" @click="handleForgotPassword">
          {{ $t('page.login.pwdLogin.forgetPassword') }}
        </a>
      </div>
      <NButton type="primary" size="large" round block :loading="authStore.loginLoading" @click="handleSubmit">
        {{ $t('common.confirm') }}
      </NButton>
      <div class="flex-y-center justify-between gap-12px">
        <NButton class="flex-1" block @click="toggleLoginModule('code-login')">
          {{ $t(loginModuleRecord['code-login']) }}
        </NButton>
        <NButton class="flex-1" block @click="toggleLoginModule('register')">
          {{ $t(loginModuleRecord.register) }}
        </NButton>
      </div>
    </NSpace>
  </NForm>
</template>

<style scoped>
.captcha-error {
  font-size: 48px;
  text-align: center;
  color: #999;
  padding: 20px;
}
</style>
